//###############
//FILE : NameServerDatabase.java
//WRITER : Daniel Lowengrub, daniel_i_l, 320687098
//EXERCISE : oop ex3 2011
//DESCRIPTION:
//See below
//###############
package oop.ex3.filemanager;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import oop.ex3.communication.SocketAddress;
import oop.ex3.protocol.sessions.FileWantServersSession;


/**
 * This implements a database that stores the nameservers in a file manager.
 * It's also in charge of reading names from a file. The datafile should have the format:
 * ip%port
 * 
 * The database also supports the NS search operation specified in the assignment. It works in
 * the following way:
 * First the search is initialized by putting the current NS in the active list.
 * Then user can tell the database to use the NS in the active list to look for new NS. The old
 * active list is then moved to the regular NS list.
 * The user can repeat this untill the active list is empty.
 * @author daniel
 */
public class NameServerDatabase {
    //private static final String LOCAL_IP_NUM = "127.0.0.1";
    //private static final String LOCAL_IP_NAME = "localhost";
	private static final String SEPARATOR = "%";
    private LinkedHashSet<SocketAddress> _addressList;
    private LinkedHashSet<SocketAddress> _activeSet;
    //private boolean _firstSearchStep;
    
    private static final int IP_PART = 0;
    private static final int PORT_PART = 1;

    /**
     * Create a new database initilized with a certain datafile.
     * @param dataFile 
     * @throws FileNotFoundException
     * @throws IOException
     */
    public NameServerDatabase(String dataFile) throws FileNotFoundException, IOException {
        _addressList = new LinkedHashSet<SocketAddress>();
        populateListFromFile(dataFile);
    }
    
    //populate the database from datafile entries.
    private void populateListFromFile(String fileName) throws FileNotFoundException, IOException {
        BufferedReader reader = new BufferedReader(new FileReader(fileName));
        String line;
        line = reader.readLine();

        while (line != null) {
            String[] partition = line.split(SEPARATOR);
            String ip = partition[IP_PART];
            int port = Integer.parseInt(partition[PORT_PART]);

            _addressList.add(new SocketAddress(ip, port));

            line = reader.readLine();
        }
    }

    /**
     * Get a list of the current addresses.
     * @return A list of addresses.
     */
    public ArrayList<SocketAddress> getAddressList() {
        ArrayList<SocketAddress> output = new ArrayList<SocketAddress>();
        for (SocketAddress addr : _addressList)
            output.add(addr);

        return output;
    }

    /**
     * Initialize the NS search (explained above). This puts all the NS in the active list.
     */
    public void initServerSearch() {
       // _firstSearchStep = true;
        _activeSet = new LinkedHashSet<SocketAddress>();

        for (SocketAddress addr : _addressList)
            _activeSet.add(addr);
    }

    /**
     * Use the given NS connection to ask the NS on the active list for new NS. Put all the new NS
     * in the active list and move the old active list to the regular list.
     * @param nameServerConnection The NS connection to use in order to connect to other NS.
     */
    public void updateActiveSet(NameServerConnection nameServerConnection) {
        //first get a list of new servers
    	//System.out.println("updateing active set");
        LinkedHashSet<SocketAddress> newServers = new LinkedHashSet<SocketAddress>();

        for (SocketAddress addr : _activeSet) {
        	//System.out.println("sending want servers to " + addr.getPort());
            FileWantServersSession wantServers = new FileWantServersSession();
            nameServerConnection.initializeStreams(addr);
            nameServerConnection.initializeSession(wantServers);

            if (wantServers.executeSession(nameServerConnection.getInputStream(),
                    nameServerConnection.getOutputStream())) {
                newServers.addAll(wantServers.getNameServers());
            }

            nameServerConnection.closeConnection();
           // System.out.println("finished want servers from " + addr.getPort() + ":");
           // for (SocketAddress a : newServers)
           // 	System.out.println("new: " + a.getPort());
        }

        //if (!_firstSearchStep) //if this isn't the first round then add the active set to the big list
        //    _addressList.addAll(_activeSet);

        //now put all the new servers into the active set
        _activeSet.clear();
        
        //System.out.println("creating new active set with " + newServers.size() + "objects");
        for (SocketAddress addr : newServers) {
            if (!_addressList.contains(addr))
                _activeSet.add(addr);
        }

        //put the new active set in the address list
       // System.out.println("adding " + _activeSet.size() + "addresses to the address list");
        _addressList.addAll(_activeSet);
        
        //_firstSearchStep = false;

    }
    
    /**
     * Get the active set (as explained above). This should be used after a search is initialized.
     * @return The active set.
     */
    public ArrayList<SocketAddress> getActiveSet() {
        ArrayList<SocketAddress> output = new ArrayList<SocketAddress>();
        for (SocketAddress addr : _activeSet)
            output.add(addr);

        return output;
    }

    /**
     * Finish the NS search by copying the active list back to the regular list.
     */
    public void endServerSearch() {
        //if (!_firstSearchStep) //if this isn't the first round then add the active set to the big list
           // _addressList.addAll(_activeSet);
    }
}
